/*
 * Douglas Hiura Longo, 12 de Setembro de 2010.
 * 
 * twitter.com/douglashiura
 * java.inf.ufsc.br/dh
 * douglashiura.blogspot.com
 * douglashiura.parprimo.com
 * douglashiura@gmail.com
 * */
package net.douglashiura.picon;

import java.lang.reflect.InvocationTargetException;
import java.util.Deque;
import java.util.Map;

public abstract class Picon<T, O> {
	PiconStore master;

	Picon(PiconStore contexto) {
		master = contexto;
	}

	public abstract O getObjeto();

	@SuppressWarnings("unchecked")
	final public synchronized static <T> PiconStore build(Deque<Toke> emClasse)
			throws ExceptionCompilacao {
		PiconStore contexto = new PiconStore();
		while (!emClasse.isEmpty()) {
			try {
				Class<T> classe;
				classe = (Class<T>) Class.forName(emClasse.pop().value());
				emClasse.pop();
				while (!emClasse.peek().value().equals("}")) {
					String uid = emClasse.pop().value();
					PiconAtributoEntidade<T> picon = new PiconAtributoEntidade<T>(
							classe, emClasse, contexto);
					contexto.add(uid, picon.getObjeto());
				}
				emClasse.pop();

			} catch (Exception e) {
				throw new ExceptionCompilacao(e, emClasse.element());
			}
		}
		contexto.soldar();
		return contexto;

	}

	final void processaAtributo(Deque<Toke> emColchetes,
			ResolverReflexao<T> socka, T umObjeto)
			throws InstantiationException, IllegalAccessException,
			NoSuchFieldException, NoSuchMethodException,
			InvocationTargetException, ClassNotFoundException {
		emColchetes.pop();
		while (!emColchetes.isEmpty()
				&& !emColchetes.peek().value().equals("]")) {
			escolha(emColchetes, socka, umObjeto);
		}
		emColchetes.pop();
	}

	final private void escolha(Deque<Toke> emLabel,
			ResolverReflexao<T> fundidor, T umObjeto)
			throws InstantiationException, IllegalAccessException,
			NoSuchFieldException, NoSuchMethodException,
			InvocationTargetException, ClassNotFoundException {
		String label = emLabel.pop().value();
		String value = emLabel.pop().value();
		String proximo = emLabel.peek().value();
		switch (Escolha.qual(value, proximo)) {
		case REFERENCIA:
			fazerReferencia(label, emLabel, umObjeto, fundidor);
			break;
		case VALUE:
			fazerValor(fundidor, umObjeto, label, value);
			break;
		case COMPOSTO:
			fazerAtributoEntidade(emLabel, fundidor, label, umObjeto);
			break;
		case LISTA:
			fazerLista(emLabel, value, label, fundidor, umObjeto);
			break;
		}

	}

	final private void fazerValor(ResolverReflexao<T> socka, T umObjeto,
			String label, String value) throws InstantiationException,
			IllegalAccessException, NoSuchFieldException,
			NoSuchMethodException, InvocationTargetException {
		socka.colocarPrimitivo(label, value, umObjeto);
	}

	@SuppressWarnings("unchecked")
	final private void fazerLista(Deque<Toke> emValue, String value,
			String label, ResolverReflexao<T> reflect, T umObjeto)
			throws InstantiationException, IllegalAccessException,
			NoSuchFieldException, NoSuchMethodException,
			InvocationTargetException, ClassNotFoundException {
		PiconLista<Object> picon = new PiconLista<Object>(
				(Class<Object>) Class.forName(value), emValue, master);
		reflect.colocarValorComposto(label, (T) picon.getObjeto(), umObjeto);

	}

	final private void fazerAtributoEntidade(Deque<Toke> emValue,
			ResolverReflexao<T> socka, String label, T umObjeto)
			throws InstantiationException, IllegalAccessException,
			NoSuchFieldException, NoSuchMethodException,
			InvocationTargetException, ClassNotFoundException {
		PiconAtributoEntidade<Object> pi = new PiconAtributoEntidade<Object>(
				socka.getClasse(label), emValue, master);
		socka.colocarValorComposto(label, pi.getObjeto(), umObjeto);
	}

	final private void fazerReferencia(final String label,
			final Deque<Toke> value, final T umObjeto,
			final ResolverReflexao<T> socka) {
		final Toke toke = value.pop();
		final String uid = toke.value();
		master.addReferencia(new Link() {
			public void processar(Map<String, Object> referenciaveis)
					throws InstantiationException, IllegalAccessException,
					NoSuchFieldException, SecurityException,
					NoSuchMethodException, IllegalArgumentException,
					InvocationTargetException {
				socka.colocarValorComposto(label, referenciaveis.get(uid),
						umObjeto);
			}

			@Override
			public Toke getToke() {
				return toke;
			}
		});

	}

	public enum Escolha {
		REFERENCIA, VALUE, COMPOSTO, LISTA;
		final public static Escolha qual(String value, String proximo) {
			if (value.equals("#"))
				return REFERENCIA;
			else if (proximo.equals("["))
				return COMPOSTO;
			else if (proximo.equals("{"))
				return LISTA;
			else
				return VALUE;
		}
	}

}
